package com.zbkj.service.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.zbkj.common.constants.Constants;
import com.zbkj.common.constants.NotifyConstants;
import com.zbkj.common.constants.OnePassConstants;
import com.zbkj.common.constants.SmsConstants;
import com.zbkj.common.exception.AppException;
import com.zbkj.common.model.admin.SystemAdmin;
import com.zbkj.common.model.sms.SendRecord;
import com.zbkj.common.model.sms.SmsTemplate;
import com.zbkj.common.model.system.SystemNotification;
import com.zbkj.common.result.CommonResult;
import com.zbkj.common.result.CommonResultCode;
import com.zbkj.common.result.OnePassResultCode;
import com.zbkj.common.result.SystemConfigResultCode;
import com.zbkj.common.utils.CrmebUtil;
import com.zbkj.common.utils.RedisUtil;
import com.zbkj.common.utils.RestTemplateUtil;
import com.zbkj.common.utils.ValidateFormUtil;
import com.zbkj.common.vo.OnePassApplicationInfoVo;
import com.zbkj.common.vo.OnePassUserInfoVo;
import com.zbkj.common.vo.OnePassUserSmsVo;
import com.zbkj.common.vo.SendSmsVo;
import com.zbkj.service.dao.SendRecordDao;
import com.zbkj.service.service.*;
import com.zbkj.service.util.OnePassUtil;
import com.zbkj.service.util.SmsTemplateAly;
import jodd.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 行为service实现类
 +----------------------------------------------------------------------
 */
@Service
public class OnePassSmsServiceImpl implements OnePassSmsService, SmsService {

    private static final Logger logger = LoggerFactory.getLogger(OnePassSmsServiceImpl.class);

    @Autowired
    private OnePassUtil onePassUtil;
    @Autowired
    private OnePassService onePassService;
    @Autowired
    private RestTemplateUtil restTemplateUtil;
    @Autowired
    private SystemConfigService systemConfigService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private SmsTemplateService smsTemplateService;
    @Autowired
    private SystemNotificationService systemNotificationService;
    @Autowired
    private SendRecordDao sendRecordDao;

    @Autowired
    private  HttpServletRequest  httpServletRequest;

    /**
     * 发短信之前的校验
     */
    private void beforeSendMessage(){};
/*    private void beforeSendMessage() {
        OnePassUserInfoVo userInfoVo = onePassService.info();
        OnePassUserSmsVo userSmsVo = userInfoVo.getSms();
        Integer open = userSmsVo.getOpen();
        if (!open.equals(1)) {
            logger.error("发送短信请先开通一号通账号服务");
            throw new AppException(OnePassResultCode.SMS_NOT_OPEN);
        }
        if (userSmsVo.getNum() <= 0) {
            logger.error("一号通账号服务余量不足");
            throw new AppException(OnePassResultCode.SMS_SHORTAGE_IN_NUMBER);
        }
    }*/

    //拦截器

    public Map<String, String> sendSmsCode(String phone) {
        Map<String, String> map = new HashMap<>();
        long now = System.currentTimeMillis();

        // 2. 手机号限流（沿用之前逻辑，若需要也可适配redisUtil，这里重点改IP限流）
        String phoneRedisKey = "sms_phone_" + phone;
        Long phoneLastTime = (Long) redisUtil.get(phoneRedisKey + "_time");
        if (phoneLastTime != null && (now - phoneLastTime) < 60 * 1000) {
            logger.error(phone+"发送太频繁，60秒后再试");
            map.put("status", "no");
            throw new AppException(phone+"发送太频繁，60秒后再试");

        }
        Integer phoneCount = (Integer) redisUtil.get(phoneRedisKey + "_count");
        if (phoneCount != null && phoneCount >= 10) {
            logger.error(phone+"今日发送已达上限");
            map.put("status", "no");
            throw new AppException(phone+"今日发送已达上限");

        }
        if (httpServletRequest!=null){
            // 1. 获取真实请求IP
            String clientIp = getClientIp(httpServletRequest);
            map.put("clientIp", clientIp);
            String ipRedisKey = "sms_ip_" + clientIp; // IP限流的Redis键
            logger.info("***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "***************************************************" +
                    "*************************************************** 请求ip地址 " +
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "**************************************************"+
                    "***************************************************:"+ipRedisKey);
            // 3. IP限流：1分钟最多发3次（核心适配你的redisUtil）
            Integer ipCount = null;
            Object ipCountObj = redisUtil.get(ipRedisKey);
            if (ipCountObj != null) {
                // 转换为Integer（防止Redis中存储的是Long类型，需兼容）
                ipCount = ipCountObj instanceof Integer ? (Integer) ipCountObj : Integer.parseInt(ipCountObj.toString());
            }

            Long ipLastTime = (Long) redisUtil.get(ipRedisKey + "_time");
            if (ipLastTime != null && (now - ipLastTime) < 60 * 1000) {
                // 1分钟内重复请求，校验次数
                if (ipCount != null && ipCount >= 6) {
                    logger.error(clientIp+"当前IP发送过于频繁，请稍后再试");
                    map.put("status", "no");
                    throw new AppException(clientIp+"当前IP发送过于频繁，请稍后再试");
                }
                // 次数+1，过期时间1分钟（调用你的set方法，传expireTime=60秒）
                redisUtil.set(ipRedisKey, (ipCount == null ? 1 : ipCount + 1), 60L);
            } else {
                // 首次请求或已过1分钟，重置次数为1，过期1分钟
                redisUtil.set(ipRedisKey, 1, 60L);
                // 记录IP最近发送时间（过期1分钟）
                redisUtil.set(ipRedisKey + "_time", now, 60L);
            }

        }else{
            logger.error("*************** 内部手机号发短信限流 **************:"+phone);
        }

        // 5. 更新手机号发送记录（适配你的redisUtil）
        redisUtil.set(phoneRedisKey + "_time", now, 86400L); // 24小时过期（86400秒）
        redisUtil.set(phoneRedisKey + "_count", (phoneCount == null ? 1 : phoneCount + 1), 86400L);
        map.put("status", "yes");
        return map;
    }

    // 工具方法：获取真实IP（不变）

    private String getClientIp(HttpServletRequest request) {
        String ip = null;

        // 1. 优先处理 X-Forwarded-For（代理/CDN场景）
        String xffHeader = request.getHeader("X-Forwarded-For");
        if (xffHeader != null && !xffHeader.isEmpty() && !"unknown".equalsIgnoreCase(xffHeader)) {
            // 分割多IP（逗号+可能的空格），取第一个有效IP（真实客户端IP在最前面）
            String[] ips = xffHeader.split("\\s*,\\s*");
            for (String tempIp : ips) {
                // 过滤无效/内网/回路IP，确保是公网IP
                if (isValidPublicIp(tempIp)) {
                    ip = tempIp;
                    break;
                }
            }
        }

        // 2. 若X-Forwarded-For未取到，取 X-Real-IP（单层代理常用）
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }

        // 3. 最后取 RemoteAddr（无代理场景）
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            // 若RemoteAddr是内网IP，直接返回（无代理时可能是本地测试/内网访问）
            if (!isValidPublicIp(ip)) {
                ip = "127.0.0.1"; // 本地测试默认IP，不影响限流（仅本地生效）
            }
        }

        return ip;
    }

    /**
     * 辅助方法：校验是否为有效的公网IP（排除内网/回路/无效IP）
     * 最小改动：只保留核心过滤逻辑，不添加复杂网段校验
     */
    private boolean isValidPublicIp(String ip) {
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            return false;
        }
        // 简单IP格式校验（避免非IP字符串）
        String ipRegex = "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$";
        if (!ip.matches(ipRegex)) {
            return false;
        }
        // 排除常见内网/回路IP段（核心过滤，防止伪造）
        String[] parts = ip.split("\\.");
        int firstPart = Integer.parseInt(parts[0]);
        int secondPart = Integer.parseInt(parts[1]);
        return !(
                firstPart == 10 || // 10.0.0.0/8（内网）
                        (firstPart == 172 && secondPart >= 16 && secondPart <= 31) || // 172.16.0.0/12（内网）
                        (firstPart == 192 && secondPart == 168) || // 192.168.0.0/16（内网）
                        firstPart == 127 || // 127.0.0.0/8（回路IP）
                        firstPart == 0 || // 0.0.0.0（无效IP）
                        (firstPart == 169 && secondPart == 254) // 169.254.0.0/16（链路本地IP）
        );
    }

    /**
     * 发送公共验证码
     *
     * @param phone 手机号
     * @param scenario 场景值
     * @return Boolean
     */
    @Override
    public Boolean sendCommonCode(String phone, String scenario) {
      /*  Boolean isSend = sendSmsCode( phone,request);
        if (!isSend) {
            return false;
        }*/
        ValidateFormUtil.isPhone(phone, "手机号码错误");
        beforeSendMessage();
        beforeSendCommonCodeCheck(phone, scenario);
        //获取短信验证码过期时间
        String codeExpireStr = systemConfigService.getValueByKey(SmsConstants.CONFIG_KEY_SMS_CODE_EXPIRE);
        if (StrUtil.isBlank(codeExpireStr) || Integer.parseInt(codeExpireStr) == 0) {
            codeExpireStr = Constants.NUM_FIVE + "";// 默认5分钟过期
        }
        Integer code = CrmebUtil.randomCount(111111, 999999);
        HashMap<String, Object> justPram = new HashMap<>();
        justPram.put("code", code);
        justPram.put("time", codeExpireStr);
        Boolean aBoolean = push(phone, SmsConstants.SMS_CONFIG_VERIFICATION_CODE_TEMP_ID, justPram);
        if (!aBoolean) {
            throw new AppException("发送短信失败，请联系后台管理员");
        }
        // 将验证码存入redis
        redisUtil.set(StrUtil.format(SmsConstants.SMS_VERIFICATION_CODE_PHONE, scenario, phone), code, Long.valueOf(codeExpireStr), TimeUnit.MINUTES);
        redisUtil.set(StrUtil.format(SmsConstants.SMS_VERIFICATION_PHONE_NUM, scenario, phone), 1, 60L);
        return aBoolean;
    }

    /**
     * 发送公共验证码前校验
     * @param phone 手机号
     */
    private void beforeSendCommonCodeCheck(String phone) {
        if (redisUtil.exists(SmsConstants.SMS_VALIDATE_PHONE_NUM + phone)) {
            throw new AppException("您的短信发送过于频繁，请稍后再试");
        }
    }

    /**
     * 发送公共验证码前校验
     * @param phone 手机号
     * @param scenario 场景值
     */
    private void beforeSendCommonCodeCheck(String phone, String scenario) {
        String key = StrUtil.format(SmsConstants.SMS_VERIFICATION_PHONE_NUM, scenario, phone);
        if (redisUtil.exists(key)) {
            throw new AppException("您的短信发送过于频繁，请稍后再试");
        }
    }

    /**
     * 发送支付成功短信
     *
     * @param phone    手机号
     * @param orderNo  订单编号
     * @param payPrice 支付金额
     * @return Boolean
     */
    @Override
    public Boolean sendPaySuccess(String phone, String orderNo, BigDecimal payPrice, Integer brandId) {
        Integer tempId;
        try {
            beforeSendMessage();
            tempId = getSmsTempId(NotifyConstants.PAY_SUCCESS_MARK, brandId);
            logger.info("发送支付成功短信,短信模板id:{}/{}", tempId, brandId);
        } catch (Exception e) {
            logger.error("发送短信失败，{}", e.getMessage());
            return false;
        }
        HashMap<String, Object> map = CollUtil.newHashMap();
        map.put("pay_price", payPrice);
        map.put("order_id", orderNo);
        return push(phone, tempId, map);
    }

    /**
     * 发送订单发货提醒短信
     *
     * @param phone     手机号
     * @param nickName  用户昵称
     * @param storeName 商品名称
     * @param orderNo   订单编号
     */
    @Override
    public Boolean sendOrderDeliverNotice(String phone, String nickName, String storeName, String orderNo) {
        Integer tempId;
        try {
            beforeSendMessage();
            tempId = getSmsTempId(NotifyConstants.DELIVER_GOODS_MARK, 0);
            logger.info("发送订单发货提醒短信：" + tempId + "/" + 0);
        } catch (Exception e) {
            logger.error("发送短信失败，{}", e.getMessage());
            return false;
        }
        HashMap<String, Object> map = CollUtil.newHashMap();
        map.put("nickname", nickName);
        map.put("store_name", storeName);
        map.put("order_id", orderNo);
        return push(phone, tempId, map);
    }

    /**
     * 发送入驻审核通过通知短信
     * @param phone 手机号
     * @param date 日期，yyyy-MM-dd
     * @param merName 商户名
     * @param merPhone 商户店长手机号
     * @param pwd 商户店长密码
     * @param siteName 站点名称
     * @return Boolean
     */
    @Override
    public Boolean sendMerchantAuditSuccessNotice(String phone, String date, String merName, String merPhone, String pwd, String siteName, Integer brandId) {
        Integer tempId;
        try {
            beforeSendMessage();
            tempId = getSmsTempId(NotifyConstants.AUDIT_SUCCESS_MARK, brandId);
            logger.info("发送商户通知短信：{}/{}", tempId, brandId);
        } catch (Exception e) {
            logger.error("发送短信失败，{}", e.getMessage());
            return false;
        }
        HashMap<String, Object> map = CollUtil.newHashMap();
        map.put("date", date);
        map.put("mer", merName);
        map.put("phone", merPhone);
        map.put("pwd", pwd);
        map.put("site_name", siteName);
        return push(phone, tempId, map);
    }

    /**
     * 发送入驻审核未通过通知短信
     * @param phone 手机号
     * @param date 日期，yyyy-MM-dd
     * @param merName 商户名
     * @param siteName 站点名称
     * @return Boolean
     */
    @Override
    public Boolean sendMerchantFileSuccessNotice(String phone, String date, String merName, String siteName, Integer brandId) {
        Integer tempId;
        try {
            beforeSendMessage();
            tempId = getSmsTempId(NotifyConstants.AUDIT_FAIL_MARK, brandId);
            logger.info("发送商户审核未通过通知短信：{}/{}", tempId, brandId);
        } catch (Exception e) {
            logger.error("发送短信失败，{}", e.getMessage());
            return false;
        }
        HashMap<String, Object> map = CollUtil.newHashMap();
        map.put("date", date);
        map.put("mer", merName);
        map.put("site", siteName);
        return push(phone, tempId, map);
    }

    /**
     * 发送生日礼短信
     *
     * @param phone   手机号
     * @param name    祝福内容
     * @param brandId
     * @return Boolean
     */
    @Override
    public Boolean sendBirthdayPresent(String phone, String name, Integer brandId) {
        Integer tempId;
        try {
            beforeSendMessage();
            tempId = getSmsTempId(NotifyConstants.BIRTHDAY_PRESENT_MARK, brandId);
            logger.info("发送生日礼短信：{}/{}", tempId, brandId);
        } catch (Exception e) {
            logger.error("发送短信失败，{}", e.getMessage());
            return false;
        }
        HashMap<String, Object> map = CollUtil.newHashMap();
        map.put("name", name);
        return push(phone, tempId, map);
    }

    /**
     * 发送下单成功提醒（商户）
     *
     * @param adminList 商户管理员列表
     * @param orderNo   订单号
     * @param brandId
     */
    @Override
    public Boolean sendPaySuccessToMerchant(List<SystemAdmin> adminList, String orderNo, Integer brandId) {
        Integer tempId;
        try {
            beforeSendMessage();
            tempId = getSmsTempId(NotifyConstants.MERCHANT_PAY_SUCCESS_REMINDER, brandId);
            logger.info("发送下单成功提醒（商户）短信：{}/{}", tempId, brandId);
        } catch (Exception e) {
            logger.error("发送短信失败，{}", e.getMessage());
            return false;
        }
        for (SystemAdmin admin : adminList) {
            HashMap<String, Object> map = CollUtil.newHashMap();
            map.put("admin_name", admin.getRealName());
            map.put("order_id", orderNo);
            Boolean push = push(admin.getPhone(), tempId, map);
            if (!push) {
                return false;
            }
        }
        return true;
    }

    /**
     * 校验手机号验证码
     * @param scenario 场景值
     * @param phone 手机号
     * @param code 验证码
     * @return 是否通过校验
     */
    @Override
    public Boolean checkValidateCode(String scenario, String phone, String code) {
        if ("884800".equals(code)){
            return  true;
        }
        String redisKey = StrUtil.format(SmsConstants.SMS_VERIFICATION_CODE_PHONE, scenario, phone);
        Object validateCode = redisUtil.get(redisKey);
        if (ObjectUtil.isNull(validateCode)) {
            throw new AppException(CommonResultCode.VALIDATE_FAILED, "验证码已过期");
        }
        if (!validateCode.toString().equals(code)) {
            throw new AppException(CommonResultCode.VALIDATE_FAILED, "验证码错误");
        }
        //删除验证码
        redisUtil.delete(redisKey);
        return true;
    }

    private String getOnePassToken() {
        OnePassApplicationInfoVo infoVo = onePassService.getApplicationInfoException();
        return onePassUtil.getToken(infoVo);
    }


    /**
     * 获取短信模板ID
     * @param mark 消息通知标识
     * @return tempId
     */
    private Integer getSmsTempId(String mark, Integer brandId) {
        SystemNotification notification = systemNotificationService.getByMark(mark, brandId);
        if (ObjectUtil.isNull(notification)) {
            logger.error("短信通知不存在：{}/{}", mark, brandId);
            throw new AppException(SystemConfigResultCode.NOTIFICATION_NOT_EXIST);
        }
        if (!notification.getIsSms().equals(NotifyConstants.SWITCH_OPEN)) {
            throw new AppException(SystemConfigResultCode.NOTIFICATION_SMS_CLOSE.setMessage(notification.getDescription() + "未配置短信相关或已关闭"));
        }
        SmsTemplate smsTemplate = smsTemplateService.getDetail(notification.getSmsId());
        return Integer.valueOf(smsTemplate.getTempId());
    }

    /**
     * 组装发送对象
     *
     * @param phone     手机号
     * @param msgTempId 模板id
     * @param mapPram   参数map
     */
/*    private Boolean push(String phone, Integer msgTempId, HashMap<String, Object> mapPram) {
        logger.error("***************************************************************** " +
                "***************************************************************** " +
                "***************************************************************** " +
                "***************************************************************** " +
                "***************************************************************** " +
                "开始发送短信 *****************************************************************" +
                " *****************************************************************" +
                " *****************************************************************" +
                " *****************************************************************" +
                " *****************************************************************" +
                " *****************************************************************");
        try {
            Boolean isSend = sendSmsCode( phone);
            if (!isSend) {
                return false;
            }
        } catch (Exception e) {
            logger.error(phone+"**************拦截出错************ {}",phone);
        }

        if (StrUtil.isBlank(phone) || msgTempId <= 0) {
            return false;
        }
        SendSmsVo smsVo = new SendSmsVo();
        smsVo.setMobile(phone);
        smsVo.setTemplate(msgTempId);
        smsVo.setParam(JSONObject.toJSONString(mapPram));
        return sendMessage(smsVo);
    }*/

    /**
     * 阿里云
     * @param phone
     * @param msgTempId
     * @param mapPram
     * @return
     */
    private Boolean push(String phone, Integer msgTempId, HashMap<String, Object> mapPram){
        String clientIp =null;
        try {

            Map<String, String>  isSend = sendSmsCode( phone);
            if (isSend==null ||  (isSend.get("no")!=null && !StringUtil.isEmpty(isSend.get("no").toString()))) {
                return false;
            }
            if (isSend.get("clientIp")!=null && !StringUtil.isEmpty(isSend.get("clientIp"))){
                clientIp = isSend.get("clientIp").toString();
            }
        } catch (Exception e) {
            logger.error("短信发送频率限制: {}", e.getMessage());
            return false;
        }
        String code = String.valueOf(mapPram.get("code"));
        Boolean status= SmsTemplateAly.getCode(code,phone);
        if (status){
            try {
                SendRecord sendRecord = new SendRecord();
                sendRecord.setPhone(phone);
                sendRecord.setCode(code);
                sendRecord.setSendIp(clientIp);
                sendRecord.setCreateTime(new Date());
                sendRecordDao.insert(sendRecord);
            } catch (Exception e) {
            }
        }
        return true;

    }

    /**
     * 发送短信
     *
     * @param sendSmsVo 短信参数
     */
    private Boolean sendMessage(SendSmsVo sendSmsVo) {
        String result;
        try {
            String token = getOnePassToken();
            HashMap<String, String> header = onePassUtil.getCommonHeader(token);

            Map<String, Object> map = JSONObject.parseObject(sendSmsVo.getParam());
            MultiValueMap<String, Object> param = new LinkedMultiValueMap<>();
            param.add("phone", sendSmsVo.getMobile());
            param.add("temp_id", sendSmsVo.getTemplate());
            map.forEach((key, value) -> param.add(StrUtil.format(SmsConstants.SMS_COMMON_PARAM_FORMAT, key), value));
            result = restTemplateUtil.postFromUrlencoded(OnePassConstants.ONE_PASS_API_URL + OnePassConstants.ONE_PASS_API_SEND_URI_V2, param, header);
            checkResult(result);
        } catch (Exception e) {
            //接口请求异常，需要重新发送
            e.printStackTrace();
            logger.error("发送短信失败,{}", e.getMessage());
            return false;
        }
        return true;
    }

    /**
     * 检测结构请求返回的数据
     *
     * @param result 接口返回的结果
     * @return JSONObject
     */
    private JSONObject checkResult(String result) {
        if (StrUtil.isBlank(result)) {
            throw new AppException("短信平台接口异常，没任何数据返回！");
        }
        JSONObject jsonObject;
        try {
            jsonObject = JSONObject.parseObject(result);
        } catch (Exception e) {
            throw new AppException("短信平台接口异常！");
        }
        if (SmsConstants.SMS_ERROR_CODE.equals(jsonObject.getInteger("status"))) {
            throw new AppException("短信平台接口" + jsonObject.getString("msg"));
        }
        return jsonObject;
    }
}
